﻿namespace WebDevUtilities.VsixPkg.Extensions.AspTemplateConverter
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.IO;
    using System.Text;
    using EnvDTE;
    using EnvDTE80;
    using Microsoft.VisualStudio.Shell;
    using WebDevUtilities.Common;
    using WebDevUtilities.Common.HtmlAgilityPack;

    /// <summary>
    /// Defines the Worker Logic
    /// </summary>
    public class WorkerLogic
    {
        private bool removePrefixes, copyImports, useDefaultBaseClass;
        private string baseClass, nameSpace, prefix;
        private DTE2 dte;

        /// <summary>
        /// Declares the Visual Studio instance object
        /// </summary>
        public DTE2 Dte
        {
            get { return dte; }
            set { dte = value; }
        }

        #region public properties

        /// <summary>
        /// Set to true to use the default base class
        /// </summary>
        public bool UseDefaultBaseClass
        {
            get { return useDefaultBaseClass; }
            set { useDefaultBaseClass = value; }
        }

        /// <summary>
        /// Set to true to remove all prefixes
        /// </summary>
        public bool RemovePrefixes
        {
            get { return removePrefixes; }
            set { removePrefixes = value; }
        }

        /// <summary>
        /// The prefix to remove
        /// </summary>
        public string Prefix
        {
            get { return prefix; }
            set { prefix = value; }
        }

        /// <summary>
        /// Set to true to copy all imports
        /// </summary>
        public bool CopyImports 
        {
            get { return copyImports; }
            set { copyImports = value; }
        }

        /// <summary>
        /// The new namespace
        /// </summary>
        public string NameSpace
        {
            get { return nameSpace; }
            set { nameSpace = value; }
        }

        /// <summary>
        /// The new Baseclass
        /// </summary>
        public string BaseClass
        {
            get { return baseClass; }
            set { baseClass = value; }
        }

        #endregion

        public delegate void OnWorkerOutputDelegate(string output);

        /// <summary>
        /// This event fires when worker-internal messages, errors or exception were triggered
        /// </summary>
        public event OnWorkerOutputDelegate OnWorkerOutput;

        #region worker method

        /// <summary>
        /// Implements the Worker process
        /// </summary>
        /// <param name="worker"></param>
        /// <param name="e"></param>
        public void Worker(object worker, DoWorkEventArgs e)
        {
            bool success = true;
            if (dte != null)
            {
                UIHierarchy solutionExplorer = dte.ToolWindows.SolutionExplorer;
                var items = solutionExplorer.SelectedItems as Array;
                if (items != null || items.Length == 0)
                {
                    OnWorkerOutput(Resources.ConversionStarted);
                    foreach (UIHierarchyItem hierarchyItem in items)
                    {
                        ProjectItem prjItem = hierarchyItem.Object as ProjectItem;
                        string prjItemPath = prjItem.Properties.Item(Constants.ProjectItemPropertyFullPath).Value.ToString();
                        string prjItemName = prjItem.Name;
                        FileInfo fileInfo = new FileInfo(prjItemPath);
                        string fileTypeExt = fileInfo.Extension.ToLower();
                        switch (fileTypeExt)
                        {
                            case FileType.ASPX:
                            case FileType.ASCX:
                                if (removePrefixes && fileInfo.Name.ToLower().StartsWith(prefix))
                                {
                                    prjItem.Name = prjItemName.Substring(3);
                                }
                                string newPrjItemPath = prjItem.Properties.Item(Constants.ProjectItemPropertyFullPath).Value.ToString();
                                prjItem.Name = prjItem.Name + Constants.TempSuffix;
                                prjItemPath = prjItem.Properties.Item(Constants.ProjectItemPropertyFullPath).Value.ToString();
                                string className = string.Empty;
                                using (StreamReader streamReader = new StreamReader(prjItemPath))
                                {
                                    while (!streamReader.EndOfStream)
                                    {
                                        string currentLine = streamReader.ReadLine();
                                        if (currentLine.ToLower().StartsWith(Constants.AspElementControlStart.ToLower()) && currentLine.ToLower().Contains(Constants.NodeControlClassNameString.ToLower()))
                                        {
                                            string cn = currentLine.Substring(currentLine.IndexOf(Constants.NodeControlClassNameString)).Split('"')[1];
                                            if (removePrefixes && cn.ToLower().StartsWith(prefix))
                                            {
                                                className = cn.Substring(3);
                                            }
                                            else
                                            {
                                                className = cn;
                                            }
                                            break;
                                        }
                                    }
                                }

                                if (useDefaultBaseClass)
                                {
                                    switch (fileTypeExt)
                                    {
                                        case (FileType.ASCX):
                                            baseClass = Constants.ASCXLayoutDefaultBaseClass;
                                            break;
                                        case (FileType.ASPX):
                                            baseClass = Constants.ASPXLayoutDefaultBaseClass;
                                            break;
                                    }
                                }

                                // Creating the code file for the given file.
                                try
                                {
                                    if (!File.Exists(newPrjItemPath + FileType.CS))
                                    {
                                        using (FileStream destFile = File.Create(newPrjItemPath + FileType.CS))
                                        {
                                            using (TextWriter textWriter = new StreamWriter(destFile))
                                            {
                                                StringBuilder _usings = new StringBuilder();
                                                _usings.Append("\n");
                                                if (copyImports)
                                                {
                                                    using (StreamReader reader = new StreamReader(prjItemPath))
                                                    {
                                                        while (!reader.EndOfStream)
                                                        {
                                                            string currentLine = reader.ReadLine();
                                                            if (currentLine.ToLower().StartsWith(Constants.AspElementImportStart.ToLower()))
                                                            {
                                                                _usings.AppendLine(string.Format(Constants.UsingDeclaration, currentLine.Split('"')[1]));
                                                            }
                                                        }
                                                    }
                                                }
                                                HtmlDocument doc = new HtmlDocument();
                                                doc.OptionOutputAsXml = false;
                                                doc.OptionCheckSyntax = false;
                                                doc.OptionOutputOriginalCase = true;
                                                doc.Load(prjItemPath);
                                                StringBuilder stringBuilder = new StringBuilder();
                                                var nodes = doc.DocumentNode.SelectNodes(Constants.NodeServerScript);
                                                if (nodes != null)
                                                {
                                                    foreach (var node in nodes)
                                                    {
                                                        stringBuilder.Append("\n" + node.InnerText);
                                                        node.Remove();
                                                    }
                                                    doc.Save(prjItemPath);
                                                    string[] lines = stringBuilder.ToString().Split('\n');
                                                    stringBuilder = new StringBuilder();
                                                    foreach (string line in lines)
                                                    {
                                                        stringBuilder.Append("\t" + line);
                                                    }
                                                }
                                                string _code = stringBuilder.ToString();
                                                textWriter.WriteLine(string.Format(getTemplateStringFormat(fileTypeExt, TemplateType.CODEBEHIND), _usings.ToString(), nameSpace, className, baseClass, _code).CorrectLineEndings());
                                            }
                                        }
                                        OnWorkerOutput(string.Format(Resources.FileCreated, newPrjItemPath + FileType.CS));
                                    }
                                    else
                                    {
                                        OnWorkerOutput(string.Format(Resources.FileAlreadyExists, newPrjItemPath + FileType.CS));
                                    }
                                }
                                catch (Exception ex)
                                {
                                    OnWorkerOutput(ex.Message);
                                    success = false;
                                }

                                // Creating the designer file for the given file.
                                try
                                {
                                    if (!File.Exists(newPrjItemPath + FileType.DESIGNER))
                                    {
                                        using (FileStream destFile = File.Create(newPrjItemPath + FileType.DESIGNER))
                                        {
                                            using (TextWriter writer = new StreamWriter(destFile))
                                            {
                                                writer.WriteLine(string.Format(getTemplateStringFormat(fileTypeExt, TemplateType.DESIGNER), nameSpace, className).CorrectLineEndings());
                                            }
                                        }
                                        OnWorkerOutput(string.Format(Resources.FileCreated, newPrjItemPath + FileType.DESIGNER));
                                    }
                                    else
                                    {
                                        OnWorkerOutput(string.Format(Resources.FileAlreadyExists, newPrjItemPath + FileType.DESIGNER));
                                    }
                                }
                                catch (Exception ex)
                                {
                                    OnWorkerOutput(ex.Message);
                                    success = false;
                                }

                                // Creating the new Layout file (ASPX/ASCX)
                                try
                                {
                                    List<string> lines = new List<string>();
                                    using (FileStream destFile = File.Create(newPrjItemPath))
                                    {
                                        using (StreamReader streamReader = new StreamReader(prjItemPath))
                                        using (TextWriter textWriter = new StreamWriter(destFile))
                                        {
                                            textWriter.WriteLine(Constants.AspElementAssemblySharePoint);
                                            textWriter.WriteLine(Constants.AspElementAssemblyCommandUI);
                                            while (!streamReader.EndOfStream)
                                            {
                                                string currentLine = streamReader.ReadLine();
                                                if (currentLine.StartsWith(Constants.AspElementControlStart))
                                                {
                                                    string[] pathSplit = newPrjItemPath.Split('\\');
                                                    textWriter.WriteLine(string.Format(Constants.AspSnippetElementControl, pathSplit[pathSplit.Length - 1], nameSpace, className));
                                                }
                                                else
                                                {
                                                    if (!currentLine.StartsWith(Constants.AspElementAssemblyStart))
                                                    {
                                                        textWriter.WriteLine(currentLine);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    OnWorkerOutput(string.Format(Resources.FileCreated, newPrjItemPath));
                                    success = true;
                                }
                                catch (Exception ex)
                                {
                                    OnWorkerOutput(ex.Message);
                                    success = false;
                                }

                                Project proj = prjItem.ContainingProject;
                                prjItem.Delete();

                                // removing empty lines from all files
                                Helper.RemoveEmptyLines(newPrjItemPath);
                                Helper.RemoveEmptyLines(newPrjItemPath + FileType.CS);
                                Helper.RemoveEmptyLines(newPrjItemPath + FileType.DESIGNER);

                                // adding new files to projectfolder
                                ProjectItem newPrjItemTemplateFile = proj.ProjectItems.AddFromFile(newPrjItemPath);
                                OnWorkerOutput(string.Format(Resources.FileAddedToSolution, newPrjItemPath));
                                if (!newPrjItemTemplateFile.IsOpen)
                                { newPrjItemTemplateFile.Open(EnvDTE.Constants.vsViewKindCode); }
                                newPrjItemTemplateFile.Document.Activate();
                                dte.ExecuteCommand(Constants.VsDteEditFormatDocument);
                                if (!newPrjItemTemplateFile.Document.Saved)
                                { newPrjItemTemplateFile.Document.Save(); }
                                newPrjItemTemplateFile.Document.Close();
                                OnWorkerOutput(string.Format(Resources.FileFormated, newPrjItemPath));
                                proj.ProjectItems.AddFromFile(newPrjItemPath + FileType.CS);
                                OnWorkerOutput(string.Format(Resources.FileAddedToSolution, newPrjItemPath + FileType.CS));
                                proj.ProjectItems.AddFromFile(newPrjItemPath + FileType.DESIGNER);
                                OnWorkerOutput(string.Format(Resources.FileAddedToSolution, newPrjItemPath + FileType.DESIGNER));
                                break;
                            default:
                                OnWorkerOutput(Resources.NoFilesSelected + " " + Resources.SelectAspFile);
                                break;
                        }
                    }
                    if (!success)
                    {
                        OnWorkerOutput(Resources.ErrorWhileConverting);
                    }
                }
            }
            else
            {
                OnWorkerOutput(Resources.DteNotSet);
            }
        }

        #endregion

        #region private methods

        private string getTemplateStringFormat(string fileType, TemplateType templateType)
        {
            string template = string.Empty;
            switch (fileType)
            {
                case FileType.ASCX:
                    switch (templateType)
                    {
                        case TemplateType.CODEBEHIND:
                            template = Constants.ASCXCodeTemplate;
                            break;

                        case TemplateType.DESIGNER:
                            template = Constants.ASCXDesignerTemplate;
                            break;

                    }
                    break;
                case FileType.ASPX:
                    switch (templateType)
                    {
                        case TemplateType.CODEBEHIND:
                            template = Constants.ASPXCodeTemplate;
                            break;
                        case TemplateType.DESIGNER:
                            template = Constants.ASPXDesignerTemplate;
                            break;
                    }
                    break;
            }
            return template;
        }

        #endregion
    }
}